﻿///////////////////////////////////////////////////////////////////////////////
// File:        EpollReactor.cc
// Author:      671643387@qq.com
// Description:
///////////////////////////////////////////////////////////////////////////////

#include <cassert>
#include <memory>
#include <nio/base/TimeUtil.hpp>

#include "nio/net/detail/EpollReactor.hpp"
using namespace nio::net::detail;

const int MAX_EVENT_IN_ONE_LOOP = 0x4000;

EpollReactor::EpollReactor()
    : Reactor()
    , epfd_(::epoll_create(32))
    , mode_(MD_ET)
    , lastHeartbeatTime_(0)
    , sources_(512)
{

}

EpollReactor::~EpollReactor()
{
    assert(sources_.empty());
    ::close(epfd_);
}

bool EpollReactor::addSource(sock_t sock, Event events, net::EventHandler* handler)
{
    assert(sock != INVALID_SOCKET && handler != NULL);
    if (!(events & RE_ALL))
        return false;

    struct epoll_event event;
    memset(&event, 0, sizeof(event));
    if (events & RE_READ)
        event.events |= EPOLLIN;
    if (events & RE_WRITE)
        event.events |= EPOLLOUT;
    if (events & RE_ERROR)
        event.events |= EPOLLERR;
    event.data.fd = sock;
    if (mode_ == MD_ET)
        event.events |= EPOLLET;

    if (0 != ::epoll_ctl(epfd_, EPOLL_CTL_ADD, sock, &event))
        return false;

    sources_[sock] = handler;
    return true;
}

void EpollReactor::removeSource(sock_t sock)
{
    assert(sock != INVALID_SOCKET);
    ::epoll_ctl(epfd_, EPOLL_CTL_DEL, sock, NULL);
    sources_.erase(sources_.find(sock));
}

void EpollReactor::doTick(base::time_t timeout /* = 0 */)
{
    struct epoll_event events[MAX_EVENT_IN_ONE_LOOP];
    int iepoll = ::epoll_wait(epfd_, events, MAX_EVENT_IN_ONE_LOOP, timeout);

    bool doHeartbeat = false;
    base::time_t now = base::TimeUtil::fromStartup();
    if ((now - lastHeartbeatTime_) > cnfHeartbeatTime_)
    {
        lastHeartbeatTime_ = now;
        doHeartbeat = true;
    }

    if (iepoll > 0)
    {
        for (int x = 0; x < iepoll; ++x)
        {
            sock_t sock = events[x].data.fd;
            EventHandler* handler = sources_[sock];

            uint32_t fdevents = events[x].events;
            if (fdevents & EPOLLERR)
                handler->handleError();
            if (fdevents & EPOLLIN)
                handler->handleInput();
            if (fdevents & EPOLLOUT)
                handler->handleOutput();
            if (doHeartbeat)
                handler->handleHeartbeat();
        }
    }

}